#ifndef NETWORKED_H
#define NETWORKED_H

#include <list>

#include <Raknet/PacketEnumerations.h>
#include <Raknet/RakNetworkFactory.h>
#include <Raknet/RakClientInterface.h>
#include <Raknet/RakServerInterface.h>
#include <Raknet/NetworkTypes.h>
#include <Raknet/BitStream.h>
#include <Raknet/NetworkIDGenerator.h>

#include <utils/DynamicVariable.h>
#include <utils/String.h>

// Interface for something which can be read from or written to a bitstream
class Networked
{
public:
    void writeToBitStream(RakNet::BitStream& bsEvent) = 0;
    bool readFromBitStream(RakNet::BitStream& bsEvent) = 0;
};

typedef std::list<Networked*> NetList;
typedef std::list<Networked*>::iterator NetListIterator;

// Network wrapper for generic dynamic variables
template<typename T>
class NetworkedDV : public Networked, public GenericDV<T>
{
public:
    void writeToBitStream(RakNet::BitStream& bsEvent) { bsEvent.Write(m_Data); }
    bool readFromBitStream(RakNet::BitStream& bsEvent) { bsEvent.Read(m_Data); }
};
//TODO: How to make these template specialisations 'extensible'
//      (probably can't add more of them, but some other way).
//      Custom addNetVar( ) function(s)?
template<>
class NetworkedDV<String> : public Networked, public GenericDV<String>
{
public:
    void writeToBitStream(RakNet::BitStream& bsEvent)
	{
		//TODO: Implement proper String encoding
		//bsEvent.Write(m_Data);
	}
    bool readFromBitStream(RakNet::BitStream& bsEvent) { bsEvent.Read(m_Data); }
};

template<>
class NetworkedDV<Vector> : public Networked, public GenericDV<Vector>
{
public:
    void writeToBitStream(RakNet::BitStream& bsEvent)
	{
		bsEvent.Write(m_Data[0]);
		bsEvent.Write(m_Data[1]);
		bsEvent.Write(m_Data[2]);
	}
    bool readFromBitStream(RakNet::BitStream& bsEvent)
	{
		bsEvent.Read(m_Data[0]);
		bsEvent.Read(m_Data[1]);
		bsEvent.Read(m_Data[2]);
	}
};

// Interface for something which has a NetID
class NetworkIdentified
{
public:
    NetworkIdentified()
    {
        m_NetIDGenerator.SetParent((void*)this);
    }
    NetworkIDGenerator& getNetIDGenerator()      { return m_NetIDGenerator; }
protected:
    CNetIDGenerator m_NetIDGenerator;
};

// Network wrapper for a dynamic class
class NetworkedDC : public NetworkIdentified, public DynamicClass
{
public:
    CNetworkObject() : NetworkIdentified(), DynamicClass()
    {
        //NOTE: We needed to do this again because multiple inheritance does funny things with the addresses
        m_NetIDGenerator.SetParent((void*)this);
    }
    
    template<typename T>
    T& addNetVar(String sName)
    {
        Networked* newNV = new NetworkedDV<T>();
        m_pNetVars.push_back(newNV);
        m_VarMap[sName] = newNV;

    	return newNV->getData();
    }
    
    virtual void updateFromBitStream(RakNet::BitStream bsData)
    {
        for(NetListIterator iter = m_pNetVars.begin(); iter != m_pNetVars.end(); iter++) {
            iter->readFromBitStream();
        }
    }
    
    virtual void generateUpdateBitStream(RakNet::BitStream& bsObjectData)
    {
        for(NetListIterator iter = m_pNetVars.begin(); iter != m_pNetVars.end(); iter++) {
            iter->writeToBitStream();
        }
    }
    
protected:
    NetList m_pNetVars;
};

#endif
